package com.shayu.aicodemother.service.impl;

import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.ZipUtil;
import com.shayu.aicodemother.exception.BusinessException;
import com.shayu.aicodemother.exception.ErrorCode;
import com.shayu.aicodemother.exception.ThrowUtils;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.io.File;
import java.io.FileFilter;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.util.Set;

@Service
@Slf4j
public class ProjectDownloadServiceImpl implements ProjectDownloadService {

    /**
     * 需要过滤的文件和目录名称
     */
    private static final Set<String> IGNORED_NAMES = Set.of(
            "node_modules",
            ".git",
            "dist",
            "build",
            ".DS_Store",
            ".env",
            "target",
            ".mvn",
            ".idea",
            ".vscode"
    );

    /**
     * 需要过滤的文件扩展名
     */
    private static final Set<String> IGNORED_EXTENSIONS = Set.of(
            ".log",
            ".tmp",
            ".cache"
    );

    /**
     * 将代码文件打包压缩下载
     * @param projiectPath
     * @param downloadFileName
     * @param response
     */
    @Override
    public void downloadProjectAsZip(String projiectPath, String downloadFileName, HttpServletResponse response) {
        //基础校验
        ThrowUtils.throwIf(StrUtil.isBlank(projiectPath), ErrorCode.PARAMS_ERROR, "项目路径不存在");
        ThrowUtils.throwIf(StrUtil.isBlank(downloadFileName), ErrorCode.PARAMS_ERROR, "项目下载名称不能为空");
        File projectDir = new File(projiectPath);
        ThrowUtils.throwIf(!projectDir.exists(), ErrorCode.NOT_FOUND_ERROR, "项目目录不存在");
        ThrowUtils.throwIf(!projectDir.isDirectory(), ErrorCode.PARAMS_ERROR, "指定路径不是目录");
        log.info("开始打包下载项目：{}->{}.zip", projectDir, downloadFileName);
        //设置响应头
        response.setStatus(HttpServletResponse.SC_OK);
        response.setContentType("application/zip");
        response.addHeader("Content-Disposition",
                String.format("attachment; filename=\"%s.zip\"", downloadFileName));
        //定义文件过滤器
        //被传给了 listFiles(filter)，于是系统会遍历 projectDir 里的所有直接子文件/子目录，
        FileFilter fileFilter = file -> isPathAllowed(projectDir.toPath(), file.toPath());
        try {
            //使用Hutool的zipUtil直接过滤后的目录压缩到输出流中
            //把 projectDir 目录下符合条件的文件打包成 zip 格式，使用 UTF-8 编码写入响应输出流，并返回给用户下载。
            ZipUtil.zip(response.getOutputStream(), StandardCharsets.UTF_8, false, fileFilter, projectDir);
            log.info("项目打包下载完成：{}", downloadFileName);
        } catch (Exception e) {
            log.error("项目打包下载异常", e);
            throw new BusinessException(ErrorCode.SYSTEM_ERROR, "项目打包下载失败");
        }

    }


    /**
     * 检查路径是否允许包含在压缩包中
     *
     * @param projectRoot 项目根目录
     * @param fullPath    完整路径
     * @return 是否允许
     */
    @Override
    public boolean isPathAllowed(Path projectRoot, Path fullPath) {
        // 获取相对路径
        Path relativePath = projectRoot.relativize(fullPath);
        // 检查路径中的每一部分
        //因为 Path 类型本身支持迭代，所以 for (Path part : relativePath)
        // 会自动把路径拆分成「每一级目录 + 文件名」，逐个检查，
        // 这样就能对路径中 任意一层目录或文件做过滤判断。
        for (Path part : relativePath) {
            String partName = part.toString();
            // 检查是否在忽略名称列表中
            if (IGNORED_NAMES.contains(partName)) {
                return false;
            }
            // 检查文件扩展名
            if (IGNORED_EXTENSIONS.stream().anyMatch(partName::endsWith)) {
                return false;
            }
            log.info("该目录或文件可以下载，{}",partName);
        }
        return true;
    }
}
